#include "ReferenceProxyModel.h"



#include <ItemModel.h>
#include <ItemReference.h>
#include <ItemGroup.h>



#include <QDebug>



ReferenceProxyModel::ReferenceProxyModel(QObject *parent) :
    AbstractTreeProxyModel(parent)
{
}



void ReferenceProxyModel::setSourceTarget(const QModelIndex& sourceIndex)
{
    qDebug() << Q_FUNC_INFO;
    emit layoutAboutToBeChanged();

    m_sourceTarget = sourceIndex;

    emit layoutChanged();
}



int ReferenceProxyModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return (Field_LAST - Field_FIRST + 1);
}



QVariant ReferenceProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    Q_UNUSED(orientation);
    if (role != Qt::DisplayRole) return QVariant();

    switch (section)
    {
    case NameField:  return tr("Name");
    }

    return QVariant();
}



QVariant ReferenceProxyModel::data(const QModelIndex &proxyIndex, int role) const
{
    //qDebug() << Q_FUNC_INFO;

    QModelIndex sourceIndex = mapToSource(proxyIndex);
    if (!sourceIndex.isValid()) return QVariant();

    switch (role)
    {
    case Qt::DisplayRole:
        {
            return sourceIndex.data(role);
        }
    case Qt::EditRole:
        {
            return sourceIndex.data(role);
        }
    case Qt::CheckStateRole:
        {
            return mapToSourceReference(proxyIndex).isValid();
        }
    default:
        {
            return QVariant();
        }
    }
}



// Row definition
// Name (CB) | Value
void ReferenceProxyModel::remap()
{
    clear();

    ItemModel* model = dynamic_cast<ItemModel*>(sourceModel());
    if (!model) return;

    QModelIndex sourceGroupName = model->index(0,AbstractItem::NameData, QModelIndex());
    while (sourceGroupName.isValid())
    {
        QModelIndex proxyGroupName = createIndex(sourceGroupName.row(), NameField, sourceGroupName.internalPointer());
        addMapping(sourceGroupName, proxyGroupName);

        QModelIndex sourceItemName = sourceGroupName.child(0, AbstractItem::NameData);
        while (sourceItemName.isValid())
        {
            QModelIndex proxyItemName = createIndex(sourceItemName.row(), NameField, sourceItemName.internalPointer());
            addChildMapping(proxyGroupName, sourceItemName, proxyItemName);

            sourceItemName = sourceItemName.sibling(sourceItemName.row()+1, AbstractItem::NameData);
        }

        sourceGroupName = sourceGroupName.sibling(sourceGroupName.row()+1, AbstractItem::NameData);
    }

}



QModelIndex ReferenceProxyModel::mapToSourceReference(const QModelIndex &proxyIndex) const
{
    if (!m_sourceTarget.isValid() || !proxyIndex.isValid()) return QModelIndex();

    // proxy name should contain mapping to source index which id is checked for reference.
    QModelIndex proxyName = proxyIndex.sibling(proxyIndex.row(), NameField);
    if (!proxyName.isValid()) return QModelIndex();

    // index mapped to proxy name
    QModelIndex sourceIndex = mapToSource(proxyName);
    if (!sourceIndex.isValid()) return QModelIndex();

    // we can get id index using mapped source index
    QModelIndex sourceId = sourceIndex.sibling(sourceIndex.row(), AbstractItem::IdData);
    if (!sourceId.isValid()) return QModelIndex();

    // iterate through target's children, and compare found source reference data to source id data.
    QModelIndex sourceRef = m_sourceTarget.child(0, ItemReference::RefData);
    while (sourceRef.isValid())
    {
        if (sourceRef.data(Qt::EditRole).toString() == sourceId.data(Qt::EditRole).toString()) return sourceRef;

        sourceRef = sourceRef.sibling(sourceRef.row()+1, sourceRef.column());
    }

    return QModelIndex();
}



QModelIndex ReferenceProxyModel::createReference(const QModelIndex& proxyIndex, const int& type)
{
    //qDebug() << Q_FUNC_INFO;
    if (!m_sourceTarget.isValid() || !proxyIndex.isValid()) return QModelIndex();

    //qDebug("- Model check");
    ItemModel* model = itemModel();
    if (!model) return QModelIndex();

    //qDebug("- Source reference check");
    if (mapToSourceReference(proxyIndex).isValid()) return QModelIndex();

    //qDebug("- Source index check");
    QModelIndex sourceIndex = mapToSource(proxyIndex);
    if (!sourceIndex.isValid()) return QModelIndex();

    //qDebug("- Source id check");
    QModelIndex sourceId = sourceIndex.sibling(sourceIndex.row(), AbstractItem::IdData);
    if (!sourceId.isValid()) return QModelIndex();

    emit layoutAboutToBeChanged();


    int row = model->rowCount(m_sourceTarget);
    if (!model->insertRow(row, m_sourceTarget))
    {
        qDebug() << Q_FUNC_INFO << "- Failed to create refenrence row to" << m_sourceTarget.data(Qt::EditRole).toString() << "for" << sourceId.data(Qt::EditRole).toString();
        emit layoutChanged();
        return QModelIndex();
    }


    QModelIndex sourceRef = model->index(row, ItemReference::RefData, m_sourceTarget);
    if (!sourceRef.isValid())
    {
        qDebug() << Q_FUNC_INFO << "- Failed to find refenrence value field in" << m_sourceTarget.data(Qt::EditRole).toString() << "at row" << row;
        emit layoutChanged();
        return QModelIndex();
    }

    if (!model->setData(sourceRef, sourceId.data(Qt::EditRole), Qt::EditRole))
    {
        qDebug() << Q_FUNC_INFO << " - Failed to set reference id to:" << sourceRef.data(Qt::EditRole) << ", with value:" << sourceId.data(Qt::EditRole);
    }

    QModelIndex sourceType = sourceRef.sibling(sourceRef.row(), ItemReference::TypeData);
    if (!sourceType.isValid())
    {
        qDebug() << Q_FUNC_INFO << "- Failed to find refenrence type field in" << m_sourceTarget.data(Qt::EditRole).toString() << "at row" << row;
        emit layoutChanged();
        return sourceRef;
    }

    if (!model->setData(sourceType, type, Qt::EditRole))
    {
        qDebug() << Q_FUNC_INFO << " - Failed to set reference type to:" << sourceType.data(Qt::EditRole) << ", with value:" << type;
    }

    //qDebug() << Q_FUNC_INFO << "- Successfully created all reference data for" << m_sourceTarget.data(Qt::EditRole).toString();

    emit layoutChanged();
    return sourceRef;
}



void ReferenceProxyModel::removeReference(const QModelIndex& proxyIndex)
{
    qDebug() << Q_FUNC_INFO;

    qDebug("- Index checks");
    if (!m_sourceTarget.isValid() || !proxyIndex.isValid()) return;

    qDebug("- Model check");
    ItemModel* model = itemModel();
    if (!model) return;

    qDebug("- Source ref check");
    QModelIndex sourceRef = mapToSourceReference(proxyIndex);
    if (!sourceRef.isValid())
    {
        qDebug() << Q_FUNC_INFO << "- Reference for" << proxyIndex.data(Qt::EditRole).toString() << "does not exist in" << m_sourceTarget.data(Qt::EditRole).toString();
        return;
    }

    emit layoutAboutToBeChanged();
    model->removeRow(sourceRef.row(), m_sourceTarget);
    emit layoutChanged();

}
